using System;
using System.Configuration;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;

using Model;
using BLL.WWW_WWW.Wiki;
using Model.WWW_WWW.Wiki;

namespace BLF.WWW_WWW.Wiki
{
    /// <summary>
    /// Search Tools.
    /// </summary>
    public static class SearchTools {

        /// <summary>
        /// Searches through the Title of the Pages.
        /// </summary>
        /// <param name="query">The text to search.</param>
        /// <returns>The List of Pages containing the searched text in the Title.</returns>
        public static List<PageInfo> Search(string query) {
            List<PageInfo> result = new List<PageInfo>();
			List<CategoryInfo> tmp = Pages.Instance.AllCategories;
			List<string> cats = new List<string>();
			for(int i = 0; i < tmp.Count; i++) {
				cats.Add(tmp[i].Name);
			}
			return Search(query, cats, true);
        }

		/// <summary>
		/// Searches through the Title of the Pages.
		/// </summary>
		/// <param name="query">The text to search.</param>
		/// <param name="categories">The Categories list in which to filter the search.</param>
		/// <param name="searchUncategorized">True to search through uncategorized Pages.</param>
		/// <returns>The List of Pages containing the searched text in the Title.</returns>
		public static List<PageInfo> Search(string query, List<string> categories, bool searchUncategorized) {
			List<PageInfo> result = new List<PageInfo>();

			Regex r = PrepareRegex(query);

			PageContent content;
			for(int i = 0; i < Pages.Instance.AllPages.Count; i++) 
            {
				content = Content.GetPageContent(Pages.Instance.AllPages[i], true);
				if(r.Match(content.Title).Success) 
                {
					CategoryInfo[] cats = Pages.Instance.GetCategoriesPerPage(Pages.Instance.AllPages[i]);
					if(cats.Length == 0 && searchUncategorized) 
                    {
						result.Add(Pages.Instance.AllPages[i]);
					}
					else 
                    {
						for(int k = 0; k < cats.Length; k++) 
                        {
							if(categories.Contains(cats[k].Name)) 
                            {
								result.Add(Pages.Instance.AllPages[i]);
								break;
							}
						}
					}
				}
			}

			return result;
		}

        public static List<PageInfo> Search(string query, List<string> categories, bool searchUncategorized,string l,string b)
        {
            List<PageInfo> result = new List<PageInfo>();

            Regex r = PrepareRegex(query);

            PageContent content;
            for (int i = 0; i < Pages.Instance.AllPages.Count; i++)
            {
                if (l.Trim() != "")
                {
                    if (Pages.Instance.AllPages[i].LanguageLearn != l) continue;
                }
                if (b.Trim() != "")
                {
                    if (Pages.Instance.AllPages[i].BaseLanguage != b) continue;
                }

                content = Content.GetPageContent(Pages.Instance.AllPages[i], true);
                if (r.Match(content.Title).Success)
                {
                    CategoryInfo[] cats = Pages.Instance.GetCategoriesPerPage(Pages.Instance.AllPages[i]);
                    if (cats.Length == 0 && searchUncategorized)
                    {
                        result.Add(Pages.Instance.AllPages[i]);
                    }
                    else
                    {
                        for (int k = 0; k < cats.Length; k++)
                        {
                            if (categories.Contains(cats[k].Name))
                            {
                                result.Add(Pages.Instance.AllPages[i]);
                                break;
                            }
                        }
                    }
                }
            }

            return result;
        }

		/// <summary>
		/// Searches through the Title and Content of the Pages.
		/// </summary>
		/// <param name="query">The text to search.</param>
		/// <param name="categories">The Categories list in which to filter the search.</param>
		/// <param name="searchUncategorized">True to search through uncategorized Pages.</param>
		/// <returns>The List of Pages containing the searched text in the Title and/or content.</returns>
		public static List<PageInfo> SearchFullText(string query, List<string> categories, bool searchUncategorized) {
			List<PageInfo> result = Search(query, categories, searchUncategorized);

			Regex r = PrepareRegex(query);

			PageContent content;
			for(int i = 0; i < Pages.Instance.AllPages.Count; i++) {
				content = Content.GetPageContent(Pages.Instance.AllPages[i], true);
				if(!result.Contains(Pages.Instance.AllPages[i]) && r.Match(ParseHtml(Content.GetFormattedPageContent(Pages.Instance.AllPages[i], true))).Success) {
					CategoryInfo[] cats = Pages.Instance.GetCategoriesPerPage(Pages.Instance.AllPages[i]);
					if(cats.Length == 0 && searchUncategorized) {
						result.Add(Pages.Instance.AllPages[i]);
					}
					else {
						for(int k = 0; k < cats.Length; k++) {
							if(categories.Contains(cats[k].Name)) {
								result.Add(Pages.Instance.AllPages[i]);
								break;
							}
						}
					}
				}
			}

			return result;
		}

        public static List<PageInfo> SearchFullText(string query, List<string> categories, bool searchUncategorized, string l, string b)
        {
            List<PageInfo> result = Search(query, categories, searchUncategorized);

            Regex r = PrepareRegex(query);

            PageContent content;
            for (int i = 0; i < Pages.Instance.AllPages.Count; i++)
            {
                if (l.Trim() != "")
                {
                    if (Pages.Instance.AllPages[i].LanguageLearn != l) continue;
                }
                if (b.Trim() != "")
                {
                    if (Pages.Instance.AllPages[i].BaseLanguage != b) continue;
                }
                content = Content.GetPageContent(Pages.Instance.AllPages[i], true);
                if (!result.Contains(Pages.Instance.AllPages[i]) && r.Match(ParseHtml(Content.GetFormattedPageContent(Pages.Instance.AllPages[i], true))).Success)
                {
                    CategoryInfo[] cats = Pages.Instance.GetCategoriesPerPage(Pages.Instance.AllPages[i]);
                    if (cats.Length == 0 && searchUncategorized)
                    {
                        result.Add(Pages.Instance.AllPages[i]);
                    }
                    else
                    {
                        for (int k = 0; k < cats.Length; k++)
                        {
                            if (categories.Contains(cats[k].Name))
                            {
                                result.Add(Pages.Instance.AllPages[i]);
                                break;
                            }
                        }
                    }
                }
            }

            return result;
        }

		/// <summary>
		/// Prepares a Regex from a query.
		/// </summary>
		/// <param name="query">The query.</param>
		/// <returns>The Regex.</returns>
		private static Regex PrepareRegex(string query) {
			//string[] words;
			Regex r = null;

			query = query.Trim().Replace("  ", " ");

			StringBuilder sb = new StringBuilder();
			// Replace Regex special characters
			sb.Append(EscapeRegexChars(query));			

			if(query.StartsWith("\"") && query.EndsWith("\"")) {
				// Exact phrase
				sb.Remove(sb.Length - 1, 1);
				sb.Remove(0, 1);
				sb.Insert(0, @"\b(");
				sb.Append(@")\b");
				sb.Replace(" ", @")\b[\s\r\n]+\b(");
				r = new Regex(sb.ToString(), RegexOptions.IgnoreCase);
			}
			else {
				sb.Insert(0, @"(\b(");
				sb.Append(@")\b)");
				sb.Replace(" ", @")\b)|(\b(");
				r = new Regex(sb.ToString(), RegexOptions.IgnoreCase);
			}
			return r;
		}

		private static string ParseHtml(string html) {
			StringBuilder sb = new StringBuilder(Regex.Replace(html, "<[^>]*>", " "));
			sb.Replace("&nbsp;", " ");
			sb.Replace("  ", " ");
			return sb.ToString();
		}

		private static string EscapeRegexChars(string input) {
			StringBuilder sb = new StringBuilder(input);
			sb.Replace(@"\", @"\\"); // This must be done BEFORE all others
			sb.Replace("(", @"\(");
			sb.Replace(")", @"\)");
			sb.Replace("[", @"\[");
			sb.Replace("]", @"\]");
			sb.Replace("{", @"\{");
			sb.Replace("}", @"\}");
			sb.Replace("/", @"\/");
			sb.Replace("^", @"\^");
			sb.Replace("$", @"\$");
			sb.Replace("?", @"\?");
			sb.Replace("+", @"\+");
			sb.Replace("*", @"\*");
			sb.Replace("#", @"\#");
			sb.Replace(".", @"\.");
			sb.Replace(":", @"\:");
			sb.Replace("<", @"\<");
			sb.Replace(">", @"\>");
			sb.Replace("=", @"\=");
			return sb.ToString();
		}

    }

}
